package org.quincy.netty.test.parser;

import java.util.BitSet;
import java.util.Map;

import org.quincy.netty.test.comm.TerminalChannel;
import org.quincy.rock.comm.AbstractMessageHeadParser;
import org.quincy.rock.comm.CommunicateException;
import org.quincy.rock.comm.netty.NettyUtil;
import org.quincy.rock.comm.util.CommUtils;
import org.quincy.rock.core.util.StringUtil;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;

/**
 * <b>报文头解析器。</b>
 * <p><b>详细说明：</b></p>
 * <!-- 在此添加详细说明 -->
 * 无。
 * <p><b>修改列表：</b></p>
 * <table width="100%" cellSpacing=1 cellPadding=3 border=1>
 * <tr bgcolor="#CCCCFF"><td>序号</td><td>作者</td><td>修改日期</td><td>修改内容</td></tr>
 * <!-- 在此添加修改列表，参考第一行内容 -->
 * <tr><td>1</td><td>wks</td><td>2018年5月14日 下午1:28:10</td><td>建立类型</td></tr>
 * 
 * </table>
 * @version 1.0
 * @author wks
 * @since 1.0
 */
public class MessageHeadParser extends AbstractMessageHeadParser<ByteBuf, ByteBuf> {
	/**
	 * allocator。
	 */
	private ByteBufAllocator allocator;

	/**
	 * 内容拦截器。
	 */
	private ContentInterceptor contentInterceptor = new ContentInterceptor() {

		@Override
		public ByteBuf interceptUnpack(TerminalChannel channel, boolean encrypt, int funcCode, ByteBuf content) {
			return Unpooled.copiedBuffer(content);
		}

		@Override
		public ByteBuf interceptPack(TerminalChannel channel, boolean encrypt, int funcCode, ByteBuf content) {
			return content;
		}

		@Override
		public boolean isEncrypt(TerminalChannel channel, int funcCode) {
			return false;
		}

	};

	/**
	 * <b>构造方法。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param defaultContentType
	 */
	public MessageHeadParser(String defaultContentType) {
		super(defaultContentType);
	}

	/**
	 * <b>返回分配器。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @return 分配器
	 */
	private ByteBufAllocator alloc() {
		return allocator == null ? PooledByteBufAllocator.DEFAULT : allocator;
	}

	/**
	 * <b>设置内容拦截器。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param contentInterceptor 内容拦截器
	 */
	public void setContentInterceptor(ContentInterceptor contentInterceptor) {
		this.contentInterceptor = contentInterceptor;
	}

	/** 
	 * pack。
	 * @see org.quincy.rock.comm.MessageHeadParser#pack(java.lang.Object, java.util.Map)
	 */
	@Override
	public ByteBuf pack(ByteBuf content, Map<String, Object> ctx) {
		ByteBufAllocator alloc = alloc();
		String contentType = ensureContentType(ctx.get(CommUtils.COMM_MSG_TYPE_KEY));
		boolean isBinary = CommUtils.MESSAGE_TYPE_BINARY.equals(contentType);
		int funcCode = (Integer) ctx.get(CommUtils.COMM_FUNCTION_CODE_KEY);
		TerminalChannel channel = (TerminalChannel) ctx.get(CommUtils.COMM_CHANNEL_KEY);
		channel.setRequest(funcCode < 20000);
		int msgId = (Integer) ctx.get(CommUtils.COMM_MSG_ID_KEY);
		//是否忽略返回响应
		Boolean ignoreRespone = (Boolean) ctx.get(CommUtils.COMM_MSG_IGNORE_RESPONE_KEY);
		//版本号
		String ver = (String) ctx.get(CommUtils.COMM_MSG_PROTOCOL_VERSION_KEY);
		//时间戳
		Long time = (Long) ctx.get(CommUtils.COMM_MSG_TIMESTAMP_KEY);
		//本条报文是否要加密
		boolean encrypt = contentInterceptor.isEncrypt(channel, funcCode);
		//拦截加密
		content = contentInterceptor.interceptPack(channel, encrypt, funcCode, content);

		//报文头
		ByteBuf header = alloc.heapBuffer(30);
		header.writeByte('A');
		//标识位
		BitSet bitSet = new BitSet(8);
		bitSet.set(0, Boolean.TRUE.equals(ignoreRespone)); //是否忽略响应		
		bitSet.set(1, isBinary); //正文格式
		bitSet.set(5, encrypt); //是否加密
		header.writeByte(bitSet.toByteArray()[0]);
		//消息id	
		header.writeInt(msgId);
		//功能码		
		header.writeShort(funcCode);
		//发送方	
		header.writeByte(channel.getLocalType());
		NettyUtil.writeChars(header, channel.getLocalCode(), 10);
		//版本号
		int ver1 = 1;
		int ver2 = 0;
		if (!StringUtil.isBlank(ver)) {
			int index = ver.indexOf('.');
			if (index == -1) {
				ver1 = Integer.parseInt(ver);
			} else {
				ver1 = Integer.parseInt(ver.substring(0, index));
				ver1 = Integer.parseInt(ver.substring(index + 1));
			}
		}
		header.writeByte(ver1 << 4 + ver2);
		//时间戳
		NettyUtil.writeTimestamp(header, time == null ? System.currentTimeMillis() : time.longValue());
		//分包
		Integer pkg_total = (Integer) ctx.get(CommUtils.COMM_MSG_PKG_TOTAL_KEY);
		header.writeByte(pkg_total == null ? 1 : pkg_total.intValue());
		Integer pkg_index = (Integer) ctx.get(CommUtils.COMM_MSG_PKG_INDEX_KEY);
		header.writeByte(pkg_index == null ? 0 : pkg_index.intValue());
		//组合
		CompositeByteBuf message = alloc.compositeBuffer();
		message.addComponents(true, header);
		message.addComponents(true, content);
		//返回
		return message;
	}

	/** 
	 * unpack。
	 * @see org.quincy.rock.comm.MessageHeadParser#unpack(java.lang.Object, java.util.Map)
	 */
	@Override
	public ByteBuf unpack(ByteBuf message, Map<String, Object> ctx) {
		if (allocator == null)
			allocator = message.alloc();
		if (message.readByte() != 'A')
			throw new CommunicateException("非法报文!");
		//标志位
		long flag = message.readUnsignedByte();
		BitSet bitSet = BitSet.valueOf(new long[] { flag });
		//是否忽略返回响应		
		ctx.put(CommUtils.COMM_MSG_IGNORE_RESPONE_KEY, bitSet.get(0));
		//正文格式
		ctx.put(CommUtils.COMM_MSG_TYPE_KEY,
				bitSet.get(1) ? CommUtils.MESSAGE_TYPE_BINARY : CommUtils.MESSAGE_TYPE_JSON);
		//是否是加密报文
		boolean encrypt = bitSet.get(5);
		//消息id
		Integer msgId = message.readInt();
		ctx.put(CommUtils.COMM_MSG_ID_KEY, msgId);
		//功能码
		Integer funcCode = message.readUnsignedShort();
		ctx.put(CommUtils.COMM_FUNCTION_CODE_KEY, funcCode);
		//配置通道
		TerminalChannel channel = (TerminalChannel) ctx.get(CommUtils.COMM_CHANNEL_KEY);
		channel.setRequest(funcCode < 20000);
		channel.setRemoteType((int) message.readUnsignedByte());
		channel.setRemoteCode(NettyUtil.readChars(message, 10));
		//版本号
		short ver = message.readUnsignedByte();
		ctx.put(CommUtils.COMM_MSG_PROTOCOL_VERSION_KEY, (ver >> 4) + "." + (ver & 0x0F));
		//时间戳
		Long time = NettyUtil.readTimestamp(message);
		ctx.put(CommUtils.COMM_MSG_TIMESTAMP_KEY, time);
		//分包
		int pkg_total = message.readUnsignedByte();
		ctx.put(CommUtils.COMM_MSG_PKG_TOTAL_KEY, pkg_total);
		int pkg_index = message.readUnsignedByte();
		ctx.put(CommUtils.COMM_MSG_PKG_INDEX_KEY, pkg_index);
		//返回正文
		message = contentInterceptor.interceptUnpack(channel, encrypt, funcCode, message);
		return message;
	}
}
